using System;

namespace CryptoLibrary.Symmetrical.Block
{
	public sealed class MC1 : BaseBlockCipher
	{
		#region tables

		private static readonly byte[] EXPF =
			{
				1, 45, 226, 147, 190, 69, 21, 174, 120, 3, 135, 164, 184, 56, 207, 63,
				8, 103, 9, 148, 235, 38, 168, 107, 189, 24, 52, 27, 187, 191, 114, 247,
				64, 53, 72, 156, 81, 47, 59, 85, 227, 192, 159, 216, 211, 243, 141, 177,
				255, 167, 62, 220, 134, 119, 215, 166, 17, 251, 244, 186, 146, 145, 100, 131,
				241, 51, 239, 218, 44, 181, 178, 43, 136, 209, 153, 203, 140, 132, 29, 20,
				129, 151, 113, 202, 95, 163, 139, 87, 60, 130, 196, 82, 92, 28, 232, 160,
				4, 180, 133, 74, 246, 19, 84, 182, 223, 12, 26, 142, 222, 224, 57, 252,
				32, 155, 36, 78, 169, 152, 158, 171, 242, 96, 208, 108, 234, 250, 199, 217,
				0, 212, 31, 110, 67, 188, 236, 83, 137, 254, 122, 93, 73, 201, 50, 194,
				249, 154, 248, 109, 22, 219, 89, 150, 68, 233, 205, 230, 70, 66, 143, 10,
				193, 204, 185, 101, 176, 210, 198, 172, 30, 65, 98, 41, 46, 14, 116, 80,
				2, 90, 195, 37, 123, 138, 42, 91, 240, 6, 13, 71, 111, 112, 157, 126,
				16, 206, 18, 39, 213, 76, 79, 214, 121, 48, 104, 54, 117, 125, 228, 237,
				128, 106, 144, 55, 162, 94, 118, 170, 197, 127, 61, 175, 165, 229, 25, 97,
				253, 77, 124, 183, 11, 238, 173, 75, 34, 245, 231, 115, 35, 33, 200, 5,
				225, 102, 221, 179, 88, 105, 99, 86, 15, 161, 49, 149, 23, 7, 58, 40
			};

		private static readonly byte[] LOGF =
			{
				128, 0, 176, 9, 96, 239, 185, 253, 16, 18, 159, 228, 105, 186, 173, 248,
				192, 56, 194, 101, 79, 6, 148, 252, 25, 222, 106, 27, 93, 78, 168, 130,
				112, 237, 232, 236, 114, 179, 21, 195, 255, 171, 182, 71, 68, 1, 172, 37,
				201, 250, 142, 65, 26, 33, 203, 211, 13, 110, 254, 38, 88, 218, 50, 15,
				32, 169, 157, 132, 152, 5, 156, 187, 34, 140, 99, 231, 197, 225, 115, 198,
				175, 36, 91, 135, 102, 39, 247, 87, 244, 150, 177, 183, 92, 139, 213, 84,
				121, 223, 170, 246, 62, 163, 241, 17, 202, 245, 209, 23, 123, 147, 131, 188,
				189, 82, 30, 235, 174, 204, 214, 53, 8, 200, 138, 180, 226, 205, 191, 217,
				208, 80, 89, 63, 77, 98, 52, 10, 72, 136, 181, 86, 76, 46, 107, 158,
				210, 61, 60, 3, 19, 251, 151, 81, 117, 74, 145, 113, 35, 190, 118, 42,
				95, 249, 212, 85, 11, 220, 55, 49, 22, 116, 215, 119, 167, 230, 7, 219,
				164, 47, 70, 243, 97, 69, 103, 227, 12, 162, 59, 28, 133, 24, 4, 29,
				41, 160, 143, 178, 90, 216, 166, 126, 238, 141, 83, 75, 161, 154, 193, 14,
				122, 73, 165, 44, 129, 196, 199, 54, 43, 127, 67, 149, 51, 242, 108, 104,
				109, 240, 2, 40, 206, 221, 155, 234, 94, 153, 124, 20, 134, 207, 229, 66,
				184, 64, 120, 45, 58, 233, 100, 31, 146, 144, 125, 57, 111, 224, 137, 48,
				128, 0, 176, 9, 96, 239, 185, 253, 16, 18, 159, 228, 105, 186, 173, 248,
				192, 56, 194, 101, 79, 6, 148, 252, 25, 222, 106, 27, 93, 78, 168, 130,
				112, 237, 232, 236, 114, 179, 21, 195, 255, 171, 182, 71, 68, 1, 172, 37,
				201, 250, 142, 65, 26, 33, 203, 211, 13, 110, 254, 38, 88, 218, 50, 15,
				32, 169, 157, 132, 152, 5, 156, 187, 34, 140, 99, 231, 197, 225, 115, 198,
				175, 36, 91, 135, 102, 39, 247, 87, 244, 150, 177, 183, 92, 139, 213, 84,
				121, 223, 170, 246, 62, 163, 241, 17, 202, 245, 209, 23, 123, 147, 131, 188,
				189, 82, 30, 235, 174, 204, 214, 53, 8, 200, 138, 180, 226, 205, 191, 217,
				208, 80, 89, 63, 77, 98, 52, 10, 72, 136, 181, 86, 76, 46, 107, 158,
				210, 61, 60, 3, 19, 251, 151, 81, 117, 74, 145, 113, 35, 190, 118, 42,
				95, 249, 212, 85, 11, 220, 55, 49, 22, 116, 215, 119, 167, 230, 7, 219,
				164, 47, 70, 243, 97, 69, 103, 227, 12, 162, 59, 28, 133, 24, 4, 29,
				41, 160, 143, 178, 90, 216, 166, 126, 238, 141, 83, 75, 161, 154, 193, 14,
				122, 73, 165, 44, 129, 196, 199, 54, 43, 127, 67, 149, 51, 242, 108, 104,
				109, 240, 2, 40, 206, 221, 155, 234, 94, 153, 124, 20, 134, 207, 229, 66,
				184, 64, 120, 45, 58, 233, 100, 31, 146, 144, 125, 57, 111, 224, 137, 48
			};


		private static readonly UInt64[] P =
			{
				2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
				179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
				419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
				661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
				947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
				1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
				1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619
			};

		private static readonly UInt64[] T =
			{
				0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0,
				0x082efa98ec4e6c89, 0x452821e638d01377, 0xbe5466cf34e90c6c,
				0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917, 0x9216d5d98979fb1b,
				0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96,
				0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16,
				0x636920d871574e69, 0xa458fea3f4933d7e, 0x0d95748f728eb658,
				0x718bcd5882154aee, 0x7b54a41dc25a59b5, 0x9c30d5392af26013,
				0xc5d1b023286085f0, 0xca417918b8db38ef, 0x8e79dcb0603a180e,
				0x6c9e0e8bb01e8a3e, 0xd71577c1bd314b27, 0x78af2fda55605c60,
				0xe65525f3aa55ab94, 0x5748986263e81440, 0x55ca396a2aab10b6,
				0xb4cc5c341141e8ce, 0xa15486af7c72e993
			};

		#endregion

		private byte[] l_key;
		private byte[] temp;

		public MC1(string passphrase, ByteOrder order) : base(passphrase, order)
		{
		}

		public MC1(byte[] key, ByteOrder order) : base(key, order)
		{
		}

		~MC1()
		{
			Utility.Zeroize(l_key);
			Utility.Zeroize(temp);
		}

		protected override void Init()
		{
			l_key = new byte[16];
			temp = new byte[8];

			BLOCK_LENGTH = 16;
			name = "MC1";
		}

		private UInt64 F(UInt64 a, int round)
		{
			a += T[round];
			a = (a << 32) | (a >> 32);
			a *= P[l_key[round]];

			for (int i = 7; i >= 0; i--)
			{
				if ((i & 1) == 0)
					temp[i] = EXPF[(byte) a];
				else
					temp[i] = LOGF[(byte) a];

				a >>= 8;
			}

			UInt64 result = (((UInt64) temp[1] << 56) ^ ((UInt64) temp[3] << 48) ^ ((UInt64) temp[5] << 40) ^ ((UInt64) temp[7] << 32)) ^
				((UInt64) (temp[0] << 24) ^ ((UInt64) temp[2] << 16) ^ ((UInt64) temp[4] << 8) ^ ((UInt64) temp[6]));

			return result;

		}

		private void RoundE(ref UInt64 L, ref UInt64 R, int round)
		{
			UInt64 temp;

			temp = L ^ F(R, round);
			L = R;
			R = temp;
		}

		private void RoundD(ref UInt64 L, ref UInt64 R, int round)
		{
			UInt64 temp;

			temp = R ^ F(L, round);
			R = L;
			L = temp;
		}

		protected override void set_key(byte[] key)
		{
			Array.Copy(key, l_key, 16);

			l_key[0] ^= (byte) T[0];

			for (int i = 1; i < 16; i++)
				l_key[i] ^= (byte) F(T[i], i - 1);
		}

		public override void E(byte[] input, int offsetI, byte[] output, int offsetO)
		{
			UInt64 L = Utility.ByteArrayToUInt64(input, offsetI, defaultByteOrder);
			UInt64 R = Utility.ByteArrayToUInt64(input, offsetI + 8, defaultByteOrder);

			for (int i = 0; i < 16; i++)
				RoundE(ref L, ref R, i);

			Utility.UInt64ToByteArray(L, output, offsetO, defaultByteOrder);
			Utility.UInt64ToByteArray(R, output, offsetO + 8, defaultByteOrder);
		}

		public override void E(Utility.PseudoCat input, int offsetI, byte[] output, int offsetO)
		{
			UInt64 L = Utility.ByteArrayToUInt64(input, offsetI, defaultByteOrder);
			UInt64 R = Utility.ByteArrayToUInt64(input, offsetI + 8, defaultByteOrder);

			for (int i = 0; i < 16; i++)
				RoundE(ref L, ref R, i);

			Utility.UInt64ToByteArray(L, output, offsetO, defaultByteOrder);
			Utility.UInt64ToByteArray(R, output, offsetO + 8, defaultByteOrder);
		}

		public override void D(byte[] input, int offsetI, byte[] output, int offsetO)
		{
			UInt64 L = Utility.ByteArrayToUInt64(input, offsetI, defaultByteOrder);
			UInt64 R = Utility.ByteArrayToUInt64(input, offsetI + 8, defaultByteOrder);

			for (int i = 15; i >= 0; i--)
				RoundD(ref L, ref R, i);

			Utility.UInt64ToByteArray(L, output, offsetO, defaultByteOrder);
			Utility.UInt64ToByteArray(R, output, offsetO + 8, defaultByteOrder);
		}

	}
}